跳到主要内容

1.7-工具对象

Create by fall on 26 May 2021 Recently revised in 06 Apr 2023

Math

Math 用于 Number 类型数据处理。它不支持 BigInt。

方法作用
Math.abs()返回取绝对值
Math.max()传入逗号分割的多个数,返回最大的参数
Math.min()传入逗号分割的多个数,返回最小的参数
Math.round()四舍五入到最接近的整数
Math.ceil()返回向上取整的值
Math.floor()返回向下取整的值
Math.random()返回 0~1 随机数,无需传入值
Math.E常数 e≈2.71828
Math.trunc()返回该数值的整数部分
Math.sign()返回数值的类型,1 表示正数,-1 表示负数,0
Math.cbrt()返回数值立方根
Math.clz32()返回数值的 32 位无符号整数形式,即32-该数值的二进制表示位数
Math.imul(a,b)返回两个数相乘
Math.fround()返回数值的 32 位单精度浮点数形式
Math.pow(x,y)求 x 的 y 次方
Math.abs(-130) // 130
Math.max(22,66,99) // 99
Math.min(-22,66) // -22
Math.round(Math.PI) // 3
Math.round(Math.E) // 3
Math.ceil(1.1) // 2 向上取整
Math.floor(1.9) // 1 向下取整
  • Math.hypot() 返回所有数值平方和的平方根 Math.hypot(4,4,4,4) == 8
  • Math.expm1(): 返回e^n - 1
  • Math.log1p():返回 1 + n 的自然对数(Math.log(1 + n))
  • Math.log10():返回以 10 为底的 n 的对数
  • Math.log2():返回以 2 为底的n的对数
  • Math.sinh():返回 n 的双曲正弦
  • Math.cosh():返回 n 的双曲余弦
  • Math.tanh():返回 n 的双曲正切
  • Math.asinh():返回 n 的反双曲正弦
  • Math.acosh():返回 n 的反双曲余弦
  • Math.atanh():返回 n 的反双曲正切

RegExp

正则表达式,详情可见【正则】

对象特性

// 正则表达式的声明
const reg1 = new RegExp('hello','ig')
const reg2 = new RegExp(/xYz\d+/gi, i) // 如果传入了第二个参数,以第二个为准
const reg3 = RegExp('hello','ig')
const reg4 = /hello/gi
// i 表示不区分大小写,g 表示可以同时选取多个,m 表示可以换行匹配,每一行的内容独立
reg.flags // 可以返回当前正则的所有修饰符

修饰符

  • u 处理大于 \uFFFF Unicode 字符
  • i 表示不区分大小写
  • g 表示可以同时选取多个
  • m 表示可以换行匹配,每一行的内容独立
  • y 同样是全局匹配,只不过匹配必须连在一起
  • s dotAll,只要有点,可以用.代替所有字符

u 修饰符:含义为 Unicode 模式,用来正确处理大于 \uFFFFUnicode 字符。也就是说,如果待匹配的字符串中可能包含有大于 \uFFFF 的字符,就必须加上 u 修饰符,才能正确处理。

// 加上 u 修饰符才能让 . 字符正确识别大于 \uFFFF 的字符
/^.$/.test('🤣') // false
/^.$/u.test('🤣') // true

// 大括号 Unicode 字符表示法必须加上 u 修饰符
/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true

// 有 u 修饰符,量词才能正确匹配大于 \uFFFF 的字符
/🤣{2}/.test('🤣🤣') // false
/🤣{2}/u.test('🤣🤣') // true

RegExp.prototype.unicode 属性表示正则是否设置了 u 修饰符:

/🤣/.unicode   // false
/🤣/u.unicode // true

y 修饰符,与 g 修饰符类似也是全局匹配;不同的是 g 是剩余字符中匹配即可,而 y 则是必须在剩余的第一个字符开始匹配才行,所以 y 修饰符也叫黏连修饰符:

let s = 'aaa_aa_a'
let r1 = /a+/g
let r2 = /a+/y

r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]

r1.exec(s) // ["aa"]
r2.exec(s) // null

RegExp.prototype.sticky 属性表示是否设置了 y 修饰符:

/abc/y.sticky  // true

s 修饰符,开启 dotAll 模式,ES2018引入,通过 . 表示任意字符

/fall.again/.test('fall\nagain') // false
/fall.again/s.test('fall\nagain') // true

具名匹配,可以将捕获的字符串进行命名,通过命名进行快速查找

没有使用具名匹配

let day = /(\d{4})-(\d{2})-(\d{2})/
let result = day.exec('2021-08-12')
// result[0] === '2021-08-12'
// result[1] === '2021'
// result[2] === '08'
// result[3] === '12'

使用具名匹配

let day = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
let result = day.exec('2021-08-12')
// result.groups { year: "2021", month: "08", day: "12" }'

配合结构赋值

let {groups: {year, month, day}} = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/.exec('2021-08-12')
console.log(year, month, day) // 2021 08 12

后行断言 (?<=y)xx 只有在 y 后面才能匹配:

/(?<=\$)\d+/.exec('I have $100.')  // ['100']
// 否定断言: (?<!y)x,x 只有不在 y 后面才能匹配:

/(?<!\$)\d+/.exec('I have $100.') // ['00']

实例方法

  • reg.test() 测试是否符合,返回true 或者 false
  • reg.dotAll() 检测该正则是否处在 dotAll 模式
  • reg.exec() 返回符合的字符串组成的数组。

JSON

JSON 对象只有两个实例方法,即

  • JSON.stringify()
  • JSON.parse()

实例方法

JSON.stringify

基本用法:JSON.stringify(value[, replacer [, space]])

第三个参数作用是指定 space 个空格的缩进,想让代码更好看一点,就写上空格缩进(对象是引用类型,对象的调试可能会用到该参数)。

0xD8000xDFFF 之间的码点不能单独使用,所以 JSON.stringify() 对单个码点进行操作,如果符合 UTF-8 标准,会返回对应的字符,如果不满足会返回对应的码点。

let replacerFun = function (key, value) {
console.log(key, value)
if (key === 'name') { // 如果key 等于 name<直接返回 undefined
return undefined // 返回 undefined 时,该键值对直接忽略,
}
return value
}
let myIntro = {
name: 'fall',
age: 25,
like: 'FE'
}
JSON.stringify(myIntro, replacerFun) // {"age":25,"like":"FE"}

实现对象的深拷贝

let info = {
name:'fall'
age: 25,
like: 'FE'
}
function deepClone() {
return JSON.parse(JSON.stringify(myIntro))
}
let copyMe = deepClone(myIntro)
copyMe.like = 'Front End'
console.log(myIntro, copyMe)
// { age: 25, like: 'FE' } { age: 25, like: 'Front End' }

特别注意:JSON.stringify(Infinity) 之后,会存储的是 null,读取后,不是 Infinity

以下情况使用 JSON.stringify() 可能会出错,请确保使用前没有一下情况出现。

  • 转换属性值中有 toJSON 方法时。方法不会被JSON.stringify 识别,但如果是 toJSON,该方法会直接被执行,并且不会返回生成的字符串。详见例1。
  • 被转换值中有 undefined、任意的函数以及 symbol 值。详见例2、例3
  • 值为 NaN 和 Infinity。详见例2、例3
  • 以 symbol 为属性键的属性
  • 包含循环引用的对象,objA引用 objB,并且 objB 引用 objA
  • 具有不可枚举的属性值时
var num = Infinity
JSON.stringify(num) // "null"
// 例1:
let author = {
name:'fall',
age:23,
toJSON:function(){
return "成功"
}
}
JSON.stringify(author) // "\"成功\""
// 例2:
let author = {
wife:undefined,
money:Infinity,
string:NaN,
girlfriend:Symbol("she")
}
JSON.stringify(author) // "{\"money\":null,\"like\":null}"
// 例3
let nothing = [undefined,Infinity,NaN,Symbol("she")]
JSON.stringify(nothing)
// 例4
let author = Object.create(null, {
name: { value: "fall", enumerable: true },
age: { value: "23", enumerable: false },
})
JSON.stringify(author)
// {"name":"fall"}

Proxy

代理,用来定义基本操作的自定义行为,对被代理对象 target 操作之前会先进行拦截。

  • 实质上是目标对象target的包装
  • 可以通过代理对象进行操作,也可以直接对 target 进行操作,两者相互独立存在,进行操作
  • 只有通过 proxy操作时,才会被预设的方法拦截。直接操作对象,不会受到任何拦截影响。
// 创建代理对象,代理对象内有两个对象,一个是 target,表示被监听对象,另一个是 handler,表示做如何处理
// let proxy = new Proxy(target,handler)
// 代理对象的使用
let instance = new Proxy({user:'fall'},{
get(target,propKey,receiver){
return `你好啊,${target.name}`
}
})
// 在对被代理对象操作时,能进行拦截,并且获取的是代理处理后的返回值
instance.name // '你好啊,fall'

如果 handler 没有设置任何拦截,那么对实例的操作就会转发到目标对象身上(对代理赋值,直接作用于 target)

let target = {}
let proxy = new Proxy(target, {})
proxy.name = '布兰'
target.name // '布兰'

handler

  • get 用于拦截属性的读取操作
  • set 用于拦截对象的属性操作
  • has 拦截 propKey in proxy 的操作,表示属性是否存在。
  • deleteProperty 拦截 delete 操作
  • ownKeys 拦截 Object.keys() for...in 等循环
  • getOwnPropertyDescriptor 获取对象上属性的描述对象时的代理
  • defineProperty 命名对向上的属性描述时的代理
  • preventExtensions 设置防止扩展属性是的代理
  • getPrototypeOf 获取
  • isExtensible 可否扩展的
  • setPrototypeOf
  • apply
  • construct
let target={
name:'fall'
}
let proxy = new Proxy(target, {
get(target,propKey,receiver){},
// 拦截对象属性的读取,target 指的是被代理的对象,propKey 指受到操作的对象,receiver 表示该 Proxy 代理对象
set(target,propKey,value,receiver){},
// 拦截对于对象中属性值的设置,target 指的是被代理的对象,propKey 指受到操作的对象,value表示属性对应的值,receiver 表示该 Proxy 代理对象
has(target,propKey){},
// 拦截 propKey in proxy 操作,返回一个布尔值
deleteProperty(target,propKey){},
// 拦截删除 propKey 的操作
ownKeys(target){},
// 拦截多个Object操作,返回一个数组,数组包括所有值,不同于Object.keys(),可以返回Symbol对象
})

静态方法

Proxy.revocable() 定义一个可撤销的 Proxy

let target = {}
let handler = {}
let {proxy, revoke} = Proxy.revocable(target, handler)

proxy.foo = 123
proxy.foo // 123
revoke() // 执行结构的第二个方法,就可以撤销该 proxy
proxy.foo // TypeError

示例

get 示例

let staff = {
name:'fall',
age:13
}
const proxyStaff = new Proxy(staff,{
get(target,propKey,receiver){
console.log(target) // target 指向的是 staff
console.log(propKey) // propKey 指向的是 key age
console.log(receiver) // 指向的是 proxyStaff(一般不用添加这个参数)
// return `谁让你获取这个${propKey}数据的?不给!`
return 'nothing' // 如果想要使用默认获取值的操作,使用该返回值
},
})
console.log(proxyStaff.age)
// 如果获取本身不存在的值,则返回 undefined
// 如果定义了 return 'nothing',那么
proxyStaff.career // 'nothing'

set 示例

let staff = {
name:'fall',
age:13
}
const proxyStaff = new Proxy(staff,{
set(target,propKey,value,receiver){
console.log(target) // target 指向的是 staff
console.log(propKey) // propKey 指向的是 key age
console.log(value) // 指向的是赋的值 此处为 23
console.log(receiver)// 指向代理对象,此处指向 proxyStaff
target[propKey] = value
// 该关键字不需要 return ,在这里无意义
}
})
proxyStaff.age = 23

has 示例

let staff = {
name:'fall',
age:13
}
const proxyStaff = new Proxy(staff,{
has(target,propKey){
console.log(target) // target 指向的是 staff
console.log(propKey) // propKey 指向的是 key age
return propKey in target
}
})
if('age' in proxyStaff){
console.log(proxyStaff.age)
}

deleteProperty 示例

let staff = {
name:'fall',
age:13
}
const proxyStaff = new Proxy(staff,{
deleteProperty(target,propKey){
return delete proxyStaff.age // 此处只能返回 true & false,表示删除是否成功
}
})
if('age' in proxyStaff){
delete proxyStaff.age
console.log(proxyStaff)
}

ownKeys 示例

let staff = {
name:'fall',
age:13
}
const proxyStaff = new Proxy(staff,{
ownKeys(target){
return Object.keys(target)
// 用于拦截 Object.getOwnPropertyNames()、Object.keys()、Object.getOwnPropertySymbols()、for...in 等循环类操作
}
})
console.log(Object.keys(proxyStaff))

getOwnPropertyDescriptor 方法

let staff = {
name:'fall',
age:13
}
const proxyStaff = new Proxy(staff,{
getOwnPropertyDescriptor(target,propKey){
return Object.getOwnPropertyDescriptor(target,propKey)
// 用于拦截 Object.getOwnPropertyDescriptor()
}
})
console.log(Object.getOwnPropertyDescriptor(proxyStaff,'name'))

defineProperty 方法

let staff = {
name:'fall',
age:13
}
const proxyStaff = new Proxy(staff,{
defineProperty(target,propKey,propKeyDesc){
return Object.defineProperty(target,propKey,propKeyDesc)
// 用于拦截 Object.defineProperty() propKeyDesc 是可定义的对象的属性
}
})
console.log(Object.defineProperty(proxyStaff,'hobby',{value:'beauty'}))

preventExtension 方法

let staff = {
name:'fall'
}
const proxyStaff = new Proxy(staff,{
preventExtensions(target){
return Object.preventExtensions(target)
// 用于拦截 Object.defineProperty() propKeyDesc 是可定义的对象的属性
}
})
Object.preventExtensions(proxyStaff)
proxyStaff.age = 23
console.log(proxyStaff.age) // undefined

getPrototypeOf 方法

let staff = {
name:'fall'
}
const proxyStaff = new Proxy(staff,{
getPrototypeOf(target){
return Object.getPrototypeOf(target)
}
})
console.log(Object.getPrototypeOf(proxyStaff))

isExtensible 方法

let staff = {
name:'fall'
}
const proxyStaff = new Proxy(staff,{
isExtensible(target){
return Object.isExtensible(target)// 返回一个布尔值,表示是否可以扩展
// return false // 直接返回 false 会报错
}
})
console.log(Object.isExtensible(proxyStaff))

setPrototypeOf 方法

// 调用 Object.setPrototypeOf() 时,会触发该函数
let staff = {
name:'fall',
age:13
}
const proxyStaff = new Proxy(staff,{
setPrototypeOf(target,proto){
// 设置原型方法会调用该函数
console.log(`${target}的上,设置了${proto}原型`);
return Object.setPrototypeOf(target,proto)
}
})
console.log(Object.setPrototypeOf(proxyStaff,{}))

apply 方法

// 调用 Object.setPrototypeOf() 时,会触发该函数
function app(x,y){return x+y}
const proxyStaff = new Proxy(app,{
apply(target,object,args){
// object 是 this 参数的指向
// args 是参数列表
console.log('调用了该app方法')
return app.call(object,...args)
}
})
console.log(app(5,6))

construct 方法

// 调用 Object.setPrototypeOf() 时,会触发该函数
function App(x,y){return x+y}
const proxyStaff = new Proxy(App,{
construct(target,args){
console.log(args[0]);
return new App(...args)
}
})
console.log(new proxyStaff(42,12))

Reflect

Reflect 不是一个构造函数,不能通过 new 进行调用,更像是 Math,提供了很多静态方法提供调用。

Reflect 的作用是恢复到原来的默认值操作

  • 它提供拦截 JavaScript 操作的方法。这些方法与 proxy handlers 的方法相同。
  • 设计的目的:
    • Object 属于语言内部的方法放到 Reflect 上;
    • 修改某些 Object 方法的返回结果,让其变得更合理;
    • Object 操作变成函数行为
    • Proxy handlesReflect 方法一一对应,前者用于定义自定义行为,而后者用于恢复默认行为;

静态方法

  • Reflect.apply(target, thisArgument, argumentsList) 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似;
  • Reflect.construct(target, argumentsList[, newTarget]) 对构造函数进行 new 操作,相当于执行 new target(...args)
  • Reflect.defineProperty(target, propertyKey, attributes)Object.defineProperty() 类似。如果设置成功就会返回 true
  • Reflect.deleteProperty(target, propertyKey) 作为函数的 delete 操作符,相当于执行 delete target[name]
  • Reflect.get(target, propertyKey[, receiver]) 获取对象身上某个属性的值,类似于 target[name]
  • Reflect.getOwnPropertyDescriptor(target, propertyKey) 类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符, 否则返回 undefined
  • Reflect.getPrototypeOf(target) 类似于 Object.getPrototypeOf()
  • Reflect.has(target, propertyKey) 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同;
  • Reflect.isExtensible(target) 类似于 Object.isExtensible()
  • Reflect.ownKeys(target) 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受 enumerable 影响);
  • Reflect.preventExtensions(target) 类似于 Object.preventExtensions()。返回一个 Boolean
  • Reflect.set(target, propertyKey, value[, receiver]) 将值分配给属性的函数。返回一个 Boolean,如果更新成功,则返回 true
  • Reflect.setPrototypeOf(target, prototype) 设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回 true

Proxy 和 Reflect 是成对出现的,所以合起来就有一个示例

示例

// 这段代码中有地方完蛋了,应该是reflect是用错了,看看错在哪了
let staff = {
name:'fall',
age:13
}
const proxyStaff = new Proxy(staff,{
get(target,propKey,receiver){
console.log(target) // target 指向的是 staff
console.log(propKey) // propKey 指向的是 key age
console.log(receiver) // 指向的是 proxyStaff(一般就不用添加这个参数了)
// return `谁让你获取这个${propKey}数据的?不给!`
return Reflect.get(target, propKey) // 如果想要使用默认获取值的操作,使用该返回值
},
set(target,propKey,value){
console.log(target) // target 指向的是 staff
console.log(propKey) // propKey 指向的是 key age
console.log(value) // 指向的是赋的值 此处为 23
// target[propKey] = receiver //在此处意为:将staff.age 设置为 23
return Reflect.set(target,propKey,value) // 如果想要使用默认获取值的操作,使用该返回值
},
deleteProperty(target,propKey){
console.log(target) // target 指向的是 staff
console.log(propKey) // propKey 指向的是 key age
return Reflect.deleteProperty(target,propKey) // 如果想要使用默认获取值的操作,使用该返回值
}
})

delete proxyStaff.age
proxyStaff.age= 23
console.log(proxyStaff.age);

参考文章

作者链接
Gopal1https://www.jianshu.com/p/b714dc8e068e